<?php
require dirname(dirname(__FILE__))."/lib/WxPay.Api.php";

/**
 * 
 * 刷卡支付实现类
 * 该类实现了一个刷卡支付的流程，流程如下：
 * 1、提交刷卡支付
 * 2、根据返回结果决定是否需要查询订单，如果查询之后订单还未变则需要返回查询（一般反复查10次）
 * 3、如果反复查询10订单依然不变，则发起撤销订单
 * 4、撤销订单需要循环撤销，一直撤销成功为止（注意循环次数，建议10次）
 * 
 * 该类是微信支付提供的样例程序，商户可根据自己的需求修改，或者使用lib中的api自行开发，为了防止
 * 查询时hold住后台php进程，商户查询和撤销逻辑可在前端调用
 * 
 * @author widy
 *
 */
class MicroPay
{
    /**
     * 
     * 提交刷卡支付，并且确认结果，接口比较慢
     * @param WxPayMicroPay $microPayInput
     * @throws WxpayException
     * @return 返回查询接口的结果
     */
    public function pay($microPayInput)
    {
        //①、提交被扫支付
        $result = WxPayApi::micropay($microPayInput, 5);
        //如果返回成功
        if(!array_key_exists("return_code", $result)
            || !array_key_exists("out_trade_no", $result)
            || !array_key_exists("result_code", $result))
        {
            echo "接口调用失败,请确认是否输入是否有误！";
            throw new WxPayException("接口调用失败！");
        }
        
        //签名验证
        $out_trade_no = $microPayInput->GetOut_trade_no();
        
        //②、接口调用成功，明确返回调用失败
        if($result["return_code"] == "SUCCESS" &&
           $result["result_code"] == "FAIL" && 
           $result["err_code"] != "USERPAYING" && 
           $result["err_code"] != "SYSTEMERROR")
        {
            return false;
        }

        //③、确认支付是否成功
        $queryTimes = 10;
        while($queryTimes > 0)
        {
            $succResult = 0;
            $queryResult = $this->query($out_trade_no, $succResult);
            //如果需要等待1s后继续
            if($succResult == 2){
                sleep(2);
                continue;
            } else if($succResult == 1){//查询成功
                return $queryResult;
            } else {//订单交易失败
                return false;
            }
        }
        
        //④、次确认失败，则撤销订单
        if(!$this->cancel($out_trade_no))
        {
            throw new WxpayException("撤销单失败！");
        }

        return false;
    }
    
    /**
     * 
     * 查询订单情况
     * @param string $out_trade_no  商户订单号
     * @param int $succCode         查询订单结果
     * @return 0 订单不成功，1表示订单成功，2表示继续等待
     */
    public function query($out_trade_no, &$succCode)
    {
        $queryOrderInput = new WxPayOrderQuery();
        $queryOrderInput->SetOut_trade_no($out_trade_no);
        $result = WxPayApi::orderQuery($queryOrderInput);
        
        if($result["return_code"] == "SUCCESS" 
            && $result["result_code"] == "SUCCESS")
        {
            //支付成功
            if($result["trade_state"] == "SUCCESS"){
                $succCode = 1;
                   return $result;
            }
            //用户支付中
            else if($result["trade_state"] == "USERPAYING"){
                $succCode = 2;
                return false;
            }
        }
        
        //如果返回错误码为“此交易订单号不存在”则直接认定失败
        if($result["err_code"] == "ORDERNOTEXIST")
        {
            $succCode = 0;
        } else{
            //如果是系统错误，则后续继续
            $succCode = 2;
        }
        return false;
    }
    
    /**
     * 
     * 撤销订单，如果失败会重复调用10次
     * @param string $out_trade_no
     * @param 调用深度 $depth
     */
    public function cancel($out_trade_no, $depth = 0)
    {
        if($depth > 10){
            return false;
        }
        
        $clostOrder = new WxPayReverse();
        $clostOrder->SetOut_trade_no($out_trade_no);
        $result = WxPayApi::reverse($clostOrder);
        
        //接口调用失败
        if($result["return_code"] != "SUCCESS"){
            return false;
        }
        
        //如果结果为success且不需要重新调用撤销，则表示撤销成功
        if($result["result_code"] != "SUCCESS" 
            && $result["recall"] == "N"){
            return true;
        } else if($result["recall"] == "Y") {
            return $this->cancel($out_trade_no, ++$depth);
        }
        return false;
    }
}